package gov.nasa.anml.lifted;

import java.util.ArrayList;

import gov.nasa.anml.PDDL;
import gov.nasa.anml.State;
import gov.nasa.anml.PDDL.Argument;
import gov.nasa.anml.PDDL.FloatExpression;
import gov.nasa.anml.PDDL.Time;
import gov.nasa.anml.utility.*;

// if there is any storage, it ought to be boolean

// the purpose of this class is to capture the satisficing instance of a temporal disjunction,
// and/or the extent of the interval that satisfies a timed sub-expression.  The sub-expression will have
// narrower time (a point, for example), and this class has to go find the whole interval where the sub-expression
// does not change value; or at least doesn't syntactically change value in the sense of controlling process.

public class TimeOfExpression extends IntervalExpression<SimpleBoolean> {
	public Expression<SimpleBoolean,?> e;
	
	{
		start = new Constant<SimpleFloat>(startName,Domain.floatType);
		duration = new Constant<SimpleFloat>(durationName,Domain.floatType);
		end = new Constant<SimpleFloat>(endName,Domain.floatType);
		// currently delimiting values are represented as references to Interval.constant(Before/At/After)(Bra/Ket)
		// rather than as storage locations to be written to.  So if the result of some expression figures out the right bra value, it should
		// set bra to the appropriate constant, rather than manipulating bra.value.
		bra = null; // only at or after
		ket = null; // only at or before
	}
	
	public TimeOfExpression() {
	}
	
	public TimeOfExpression(Expression<SimpleBoolean,?> e) {
		this.e = e;
	}
	
	
	public boolean apply(State p, int contextID, State c) {
		if (e.value(p) == ANMLBoolean.True) {
			// TODO: manage timing information for the instantiation that
			// allowed the expression to succeed.
			// Perhaps implement a special Conditional class that extends
			// Expression by returning a set of simple intervals 
			// for which the condition is satisfied
			return true;
		}
		return false;
	}
	
	public TypeCode typeCode() {
		return TypeCode.Boolean;
	}

	// this might be a label of a predicate, in which case it is both
	// boolean and has storage.  Otherwise not.  For that matter, allows:
	// [all] (f_foo_l : f == foo) :-> bar
	// not that the construction is obvious...because
	// ((f == foo) := bar ) := baz;
	// is legal, but non-obvious
	// is....
	// [all] ( f == (foo := ( bar := baz)));
	// legal?  
	// I guess so, albeit foo and bar must be variables too.
	// should mean:
	// [all] f == foo
	// [all] foo := bar
	// [all] bar := baz
	// I.e., fundamental operations telescope in time on the left
	// but not on the right.  If the left is atomic, the entire remaining
	// interval is consumed, which gets confusing in situations like
	// [all] f == foo := bar
	// which can be disallowed for the sake of sanity.
	// (but would mean [all) f == foo; [end] f := bar;)

	public SimpleBoolean value(State s) {
		return e.value(s);
	}

	
	public void translateDecl(PDDL pddl, Interval unit) {
		e.translateDecl(pddl,unit);

		// The subtlety here is that this might be a disjunct in the larger context.  The notion
		// of satisficing sub-expression then gets messy.  This is related to 'motivated' and multiple ways
		// of decomposing actions.
	}
	
	// The manner in which this differs from an unlabeled expression is that this has a side-effect of establishing the identity of the label
	// History<> is particularly relevant.
//	public PDDL.Expression translateExpr(PDDL pddl,CompoundTime unit) {
//		return e.translateExpr(pddl,unit);
//	}
	
	public PDDL.Expression translateExpr(PDDL pddl, Interval unit) {
		PDDL.Time part = PDDL.getPart(unit,this);
		if (part == null) {
			System.err.println("Haven't implemented non-PDDL time access (yet).");
			//unit.asPDDLAction().condition.arguments.add(pddl.FalseCondition);
			part = PDDL.Time.All; // might work?
		} else if (part == Time.Timeless) {
			part = PDDL.Time.All;
			System.err.println("A timeless timed expression? In TimeOfExpression.translateExpr(), interpreting as All.");
		}
		copyPDDL(unit);
		PDDL.Expression exp = e.translateExpr(pddl,this);
		if (e instanceof ConstantExpression<?>)
			return exp;
		switch(e.typeCode()) {
		case Boolean:
			PDDL.BooleanExpression b = (PDDL.BooleanExpression) exp;
			ArrayList<PDDL.BooleanExpression> a = new ArrayList<PDDL.BooleanExpression>();
			switch(part) {
			case Start:
			case End:
			case Interim:
				return pddl.wrap(part,b);
			case All:
				a.add(pddl.wrap(PDDL.Time.Start,b));
			case EndHalf:
				a.add(pddl.wrap(PDDL.Time.Interim,b));
				a.add(pddl.wrap(PDDL.Time.End,b));
				break;
			case StartHalf:
				a.add(pddl.wrap(PDDL.Time.Start,b));
				a.add(pddl.wrap(PDDL.Time.Interim,b));
				break;
			default:
				System.err.println("New PDDL.Time constant unaccounted for in TimedExpression.");
				return exp;
			}
			return pddl.wrap(PDDL.Op.and,a);
		case Float:
			FloatExpression f = (FloatExpression) exp;
			switch(part) {
			case Start:
			case End:
			case Interim:
				return pddl.wrap(part,f);
			case All:
			case StartHalf:
				return pddl.wrap(Time.Start,f);
			case EndHalf:
				return pddl.wrap(Time.Interim,f);
			default:
				System.err.println("New PDDL.Time constant unaccounted for in ConstantFunctionRef.");
				return exp;
			}
		default:
			System.err.println("Oops!");
			return exp;
		}
	}


}
